#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2002,2004 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
#
######################################################################
#                                                                    #
# Module: addlcsext                                                  #
#                                                                    #
######################################################################
#                                                                    #
# Description: script to install Linux Cluster Support extension     #
#              components.                                           #
#                                                                    #
# Input:                                                             #
#   -A|--agent   install Director Agent, if not present, and         #
#                LCS extension components for Director Agent.        #
#   -h|--help    show usage                                          #
#   -p|--path    colon separated list of directories to locate       #
#                CSM/Director rpm files for install                  #
#                default $0/lcsext/packages:/opt/csm/lcsext/packages)#
#   --restart    stop and start Director as necessary without        #
#                asking.                                             #
#   -S|--server  install LCS extension components for Director       #
#                Server.                                             #
#   -v|--verbose verbose output level                                #
#                                                                    #
# Output: None                                                       #
#                                                                    #
# External references:                                               #
#         ksh                                                        #
#         rpm                                                        #
#         twgstart                                                   #
#         twgend                                                     #
#         twgstat                                                    #
#                                                                    #
######################################################################
# sccsid = "@(#)94   1.31   src/csm/director/install/bin/addlcsext.perl, csm.director, csm_rameh, rameh0431a 6/16/04 16:16:44"
use strict;
use locale;
use Getopt::Long;
use File::Basename;
use File::Spec::Functions qw(:ALL);
use FindBin qw($Bin $RealBin);

BEGIN {
    $::csmpm = $ENV{'CSM_PM'} ? $ENV{'CSM_PM'} : '/opt/csm/pm';#development use
}
use lib $::csmpm;    # for normal installs
use lib dirname($0); # for CDROM  installs
use NodeUtils;
use ArchiveUtils;
use DirectorUtils;

#################################################################################
# Turn off stdout and stderr buffering so that our IMsgs and EMsgs appear in
# the order issued when makenode calls runcmd to run addlcsext, and runcmd tries
# to append addlcsext's stdout and stderr to the EMsgCMD_FAILED it issues.
# (Without out this, all our EMsgs appear first, and then our IMsgs... which
# muddles the apparent sequence of things.)
#################################################################################
select STDOUT; $| = 1;
select STDERR; $| = 1;

##################
# Global variables
##################
our $pkgs_not_installed = 0;  # Script return code
our $verbose  = 0;            # Verbose flag
our $progname = basename($0); # Name of running program
our $progpath = dirname($0);  # full path to running program

our $try_install_agent      = 0; # Install CSM/Director agent
our $try_install_server     = 0; # Install CSM/Director server
our $try_install_everything = 0; # Install all of above (default)

our $is_mgmt_svr = 0; # system is a CSM MS?
our $is_node = 0;     # system is a CSM MN?

our $installed_count  = 0; # Number of RPMs installed

our $restart_director = -1; # Default, let user decide
our $director_stopped = 0;  # Did we stop Director (agent or server)?
our $director_installed_or_upgraded = 0;  # Did we install or upgrade Dir agent?

our @search_path = ();  # Path to search for RPMS
our %RPMS        = ();  # RPMS located   (keyed by name): version release file
our %INSTALLED   = ();

our $pkgs_to_query;

# Replace this with more complicated setup we support more platforms
our $RPM = "/bin/rpm";
our $rpmquery = "$RPM --query";
our $RPMARCH;
our $required_director_version = "4.12";
our $required_csm_version = "1.4.0.0";
our %common_prerequisites = ();

# CSM Related paths
our $csmdir     = catdir(rootdir(), "opt", "csm");
our $csmbin     = catdir($csmdir,   "bin");
our $csmbin_p   = catdir($csmdir,   "csmbin");
our $csmmsgmap  = catdir($csmdir,   "msgmaps");
our $csmlcspkgs = catdir($csmdir,   "lcsext", "packages");
our $cfmdir     = catdir(rootdir(), "cfmroot");

# Messaging variables
our $MSGCAT = "csmdirector.cat"; # msg catalogue for this cmd
our $MSGMAPPATH;
if ($ENV{'CSM_MSGMAP_PATH'}) {  # Msg maps used by NodeUtils->message()
    $MSGMAPPATH = $ENV{'CSM_MSGMAP_PATH'};
}
else {
    if (-r catdir($progpath, "msgs", "csmdirector.common.map")) {
        $MSGMAPPATH = catdir($progpath, "msgs");
    }
    else {
        $MSGMAPPATH = $csmmsgmap;
    }
}

our $director_server_name =
  NodeUtils->getMessageFromCat($MSGCAT, $MSGMAPPATH, 'common', "IMsgServer");
our $director_agent_name =
  NodeUtils->getMessageFromCat($MSGCAT, $MSGMAPPATH, 'common', "IMsgAgent");
our $restarted_name = $director_agent_name;  # Name of installed component

######################################################################
# usage:
#     display usage message
######################################################################
sub usage {
    NodeUtils->message('I', "IMsgAddlcsextUsage", $progname);
}


######################################################################
# parse_args:
#     parse and verify command line arguments
######################################################################
sub parse_args {
    Getopt::Long::Configure("posix_default");
    Getopt::Long::Configure("bundling");
    Getopt::Long::Configure("no_gnu_compat");

    my ($help)    = '';

    if (!GetOptions(       "help|h" => \$help,
		       "verbose|v+" => \$verbose,
                    "path|rpms|p=s" => \@search_path,
                          "restart" => \$restart_director,

                           "agent!" => \$try_install_agent,
                                "A" => \$try_install_agent,

                          "server!" => \$try_install_server,
                                "S" => \$try_install_server))
    {
        usage(); exit(99);
    }

    $DirectorUtils::VERBOSE = $verbose;

    # handle -p foo:bar style:
    @search_path = split(/:/,join(':',@search_path));

    if ($help) { usage(); exit(0); }

    if ($try_install_agent   == 0 &&
	$try_install_server  == 0) {
        # if no options specified, try everything
        $try_install_everything = 1;
    }
}


######################################################################
# set_defaults:
#     display usage message and exit
######################################################################
sub set_defaults {
    my $addpath;
    $addpath = catdir($progpath, updir(), "lcsext" ,"packages");
    push(@search_path, $addpath);

    $addpath = catdir($csmlcspkgs);
    push(@search_path, $addpath);

    if ($verbose >= 1) {
        NodeUtils->message('I', "IMsgRPMpath", $progname);
        print join("\n", @search_path);
        print "\n\n";
    }
}


######################################################################
# install_server
#     install IBM Director Server-side LCS extension components
######################################################################
sub install_server {
    my ($version,$release) = @{$INSTALLED{"ITDServer"}};
    $restarted_name = $director_server_name;
    NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                              'I', "IMsgDirectorInstalled",
                              $progname, $director_server_name);

    my($pkgname) = "csm.director.server";
    if (ArchiveUtils->testVersion($version, ">=", $required_director_version)) {
        my($inst_type) = &inst_type($pkgname, $required_csm_version);
        my $lcssname = NodeUtils->getMessageFromCat($MSGCAT, $MSGMAPPATH, 'common', "IMsgLCSserver");
        if ($inst_type eq "none") {
            NodeUtils->message('I', "IMsgAlreadyInstalled", $progname, $lcssname);
        } elsif ($inst_type eq "NoRPM") {
            NodeUtils->message('E', "EMsgNoRPMAvailable", $pkgname);
            $pkgs_not_installed++;
        } elsif ($inst_type eq "RPMbacklevel") {
            my($available_version, $available_spin, $available_file) = @{$RPMS{$pkgname}}; 
            NodeUtils->message('E', "EMsgRPMbacklevel", $pkgname, $required_csm_version, $available_version);
            $pkgs_not_installed++;
        } else {
            if ($inst_type eq "install") {
                NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                          'I', "IMsgAddingExtension",
                                          $progname, $lcssname);
            } elsif ($inst_type eq "upgrade") {
                NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                          'I', "IMsgMigratingExtension",
                                          $progname, $lcssname);
            }
            # Stop agent during install
            if (!$director_stopped &&
                DirectorUtils::DirectorStatus() == 1 &&
	        DirectorUtils::ask_stop_director($restarted_name)) {
                $director_stopped = 1;
                DirectorUtils::DirectorStop($restarted_name);
            }
            my($rc) = &install_rpm($pkgname, "", $inst_type);
            if ($rc != 0) { $pkgs_not_installed++; }
        }

    } else {
        NodeUtils->message('E', "EMsgInvalidPrerequisiteLevel",
                           $director_server_name,$required_director_version);
        NodeUtils->message('E', "EMsgPrerequisiteNotMet", $pkgname);
        $pkgs_not_installed++;
    }
}



######################################################################
# install_agent
#     install IBM Director Agent and Agent-side LCS extension components
######################################################################
sub install_agent {

    my ($pkgname);

    if ($is_mgmt_svr) {
        # Is CSM MS software up to snuff?
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgCSMmgmtSrvr", $progname);
        my ($version, $release) = @{$INSTALLED{"csm.server"}};
        if (ArchiveUtils->testVersion($version, "<", $required_csm_version)) {
            NodeUtils->message('E', "EMsgInvalidPrerequisiteLevel",
                               "csm.server", $required_csm_version);
            exit 98;
        }
    }

    if ($is_node) {
        # Is CSM MN software up to snuff?
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgCSMnode", $progname);
        my ($version, $release) = @{$INSTALLED{"csm.client"}};
        if (ArchiveUtils->testVersion($version, "<", $required_csm_version)) {
            NodeUtils->message('E', "EMsgInvalidPrerequisiteLevel",
                               "csm.client", $required_csm_version);
            exit 98;
        }
    }

    if ( exists($INSTALLED{"ITDServer"})) {
        # Is IBM Director Server software up to snuff?
        # (We care because Director Server code has Director Agent code within)
        NodeUtils->message('I', "IMsgAlreadyInstalled",
                           $progname,  $director_agent_name);
        my ($version, $release) = @{$INSTALLED{"ITDServer"}};
        if (ArchiveUtils->testVersion($version, "<", $required_director_version)) {
            NodeUtils->message('E', "EMsgInvalidPrerequisiteLevel",
                               $director_server_name,
                               $required_director_version);
            exit 98;
        }
    } else {
        # System is not an IBM Director Server, so install or upgrade the
        # IBM Director Agent on the system if necessary.
        $pkgname = "ITDAgent";
        my($inst_type) = &inst_type($pkgname, $required_director_version);
        if ($inst_type eq "none") {
            NodeUtils->message('I', "IMsgAlreadyInstalled", $progname, $director_agent_name);
        } elsif ($inst_type eq "NoRPM") {
            NodeUtils->message('E', "EMsgNoRPMAvailable", $pkgname);
            $pkgs_not_installed++;
        } elsif ($inst_type eq "RPMbacklevel") {
            my($available_version, $available_spin, $available_file) = @{$RPMS{$pkgname}}; 
            NodeUtils->message('E', "EMsgRPMbacklevel", $pkgname, $required_director_version, $available_version);
            $pkgs_not_installed++;
        } else {
            if ($inst_type eq "install") {
                NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                          'I', "IMsgInstallingDirAgent",
                                          $progname);
            } elsif ($inst_type eq "upgrade") {
                NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                          'I', "IMsgUpgradingDirAgent",
                                          $progname);
            }

            # Stop agent during install
            if (!$director_stopped &&
                DirectorUtils::DirectorStatus() == 1 &&
                DirectorUtils::ask_stop_director($restarted_name))
            {
                $director_stopped = 1;
                DirectorUtils::DirectorStop($restarted_name);
            }

            # Call dirinstall to install the Director Agent RPMs.
            # Try $RealBin/lcsext/packages/dirinstall first (a location
            # relative to the directory this instance of addlcsext
            # is running from). This works when running addlcsext
            # directly off the CSM CD on a CSM system.
            my $dirinstcmd = catdir($RealBin, "packages", "dirinstall");
            if (!-x $dirinstcmd) {
                # if no such dirinstall is found, try /opt/csm/csmbin/dirinstall.
                # This works when running addlcsext on an installed CSM MS.
                $dirinstcmd = catdir($csmbin_p, "dirinstall");
            }

            my ($version,$release,$fn) = @{$RPMS{$pkgname}};
            my ($volume, $dir, $file) = splitpath($fn);
            my $rpmdir = catpath($volume, $dir, undef);

            my $output = `cd $rpmdir; $dirinstcmd`; # run dirinstall

            # The dirinstall command does not play nicely and does
            # not return a return code, that we can check, if install
            # failed. So we have to check for the $errorString in the output
            my $errorString = "Installation of selected components has failed.";
            my $errorLocation = index($output, $errorString);
            ($errorLocation == -1) ? ($? = 0) : ($? = 1);

            my $rc = $? << 8;
            if ($? || $rc != 0) {
                print("\n$output");
            } else {
                NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common', 'I', "IMsgSuccess");
                # Update, new ITDAgent installed
                $director_installed_or_upgraded = 1;
            }

            # update INSTALLED list
            %INSTALLED = DirectorUtils::determine_installed_sw($pkgs_to_query);
        } # end of install or upgrade of Director Agent

        # Is IBM Director Agent software up to snuff?
        if (exists($INSTALLED{$pkgname})) {
            my ($version, $release) = @{$INSTALLED{$pkgname}};
            if (ArchiveUtils->testVersion($version, "<", $required_director_version)) {
                NodeUtils->message('E', "EMsgInvalidPrerequisiteLevel",
                                   $director_agent_name, $required_director_version);
                exit 98;
            }
        } else {
            NodeUtils->message('E', "EMsgInvalidPrerequisiteLevel",
                               $director_agent_name, $required_director_version);
            NodeUtils->message('E', "EMsgPrerequisiteNotMet",
                               "csm.director.agent");
            $pkgs_not_installed++;
        }
    } # end case where system is not an IBM Director Server

    if ( $pkgs_not_installed > 0) { return; } # bail out if things aren't going well

    # Proceed now to install or upgrade the agent-side LCS extension code
    # if system is a CSM MS, or to remove old agent-side LCS extension code
    # if system is just a CSM MN. (As of CSM 1.4.0.0, we've done away with the
    # agent-side LCS extension code on CSM MNs.)
    $pkgname = "csm.director.agent";
    my ($inst_type) = &inst_type($pkgname, $required_csm_version);
    my $lcsaname = NodeUtils->getMessageFromCat($MSGCAT, $MSGMAPPATH, 'common', "IMsgLCSagent");
    if ($inst_type eq "none") {
        if ($is_mgmt_svr) {
            NodeUtils->message('I', "IMsgAlreadyInstalled", $progname, $lcsaname);
        }
    } elsif ($inst_type eq "NoRPM") {
        if ($is_mgmt_svr) {
            NodeUtils->message('E', "EMsgNoRPMAvailable", $pkgname);
            $pkgs_not_installed++;
            return;
        }
    } elsif ($inst_type eq "RPMbacklevel") {
        if ($is_mgmt_svr) {
            my($available_version, $available_spin, $available_file) = @{$RPMS{$pkgname}}; 
            NodeUtils->message('E', "EMsgRPMbacklevel", $pkgname, $required_csm_version, $available_version);
            $pkgs_not_installed++;
            return;
        }
    } else {
        if ($is_mgmt_svr && ($inst_type eq "install")) {
            NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                      'I', "IMsgAddingExtension",
                                      $progname, $lcsaname);
        } elsif ($inst_type eq "upgrade") {
            if ($is_mgmt_svr) {
                NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                          'I', "IMsgMigratingExtension",
                                          $progname, $lcsaname);
            }
        }

        if ($is_mgmt_svr) {
            # Stop agent during install
            if (!$director_stopped &&
                DirectorUtils::DirectorStatus() == 1 &&
                DirectorUtils::ask_stop_director($restarted_name))
            {
                 $director_stopped = 1;
                 DirectorUtils::DirectorStop($restarted_name);
            }
            my($rc) = &install_rpm($pkgname, "", $inst_type);
            if ($rc != 0) {
                $pkgs_not_installed++;
                return;
            }
        }
    } # end of install or upgrade of agent-side LCS extension code on CSM MS

    if ( !$is_mgmt_svr &&
         ($is_node && (($inst_type eq "none") || ($inst_type eq "upgrade"))) ){
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgRemovingOldExtension",
                                  $progname, $lcsaname);
        # Stop agent during uninstall
        if (!$director_stopped &&
            DirectorUtils::DirectorStatus() == 1 &&
            DirectorUtils::ask_stop_director($restarted_name))
        {
            $director_stopped = 1;
            DirectorUtils::DirectorStop($restarted_name);
        }
        &uninstall_rpm($pkgname);
    }
}

######################################################################
# start_director
#     Start director agent
######################################################################
sub start_director {
    if ($director_stopped) {
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgDirectorRestarting",
                                  $progname, $restarted_name);
    }
    else {
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgDirectorStarting",
                                  $progname, $restarted_name);
    }

    DirectorUtils::DirectorStart();
}


######################################################################
# updateRPMKey
#     Determine if a given RPM is newer than previous versions located
######################################################################
sub updateRPMKey {
    my($key, $file) = @_;
    my($currentVersion, $currentRelease, $currentFile);

    # Query the characteristics of this package
    my($queryp) = "-p $file";
    print "$DirectorUtils::rpmquery $DirectorUtils::queryfmt $queryp\n" if $verbose >= 3;
    chomp(my($output) = `$DirectorUtils::rpmquery $DirectorUtils::queryfmt $queryp`);
    my(@rpmout) = split(/ /,$output);
    my($rc) = $? >> 8;
    if ($rc != 0) {
        return;
    }
    my($name,$version,$release,$arch,@rest) = @rpmout;
    NodeUtils->message('I', "IMsgPkgInfo",
                       $file, $name, $version, $release, $arch, @rest)
      if $verbose >= 2;

    # Sanity check: make sure RPM matches key
    if ($key ne $name) { return; }

    # Make sure RPM is for the architecture we're interested in
    if ($arch ne $RPMARCH) { return; }

    # Compare with previous highest version found
    if (exists($RPMS{$key})) {
        ($currentVersion, $currentRelease, $currentFile) = @{$RPMS{$key}};
        if (ArchiveUtils->testVersion($version, "<", $currentVersion, $release, $currentRelease)) {
            return;
        }
    }

    # At this point, we have an RPM of the correct architecture
    # that has a higher version than the one we had

    # Add it to the hash
    $RPMS{$key} = [ $version, $release, $file];
}


######################################################################
# catalogRPMS
#     Locate all Director & csm.director rpms
######################################################################
sub catalogRPMS {
    my($sp);
    foreach $sp (@search_path) {
        if (!opendir(THEDIR, $sp)) {
            NodeUtils->message('I', "IMsgRPMSearchPath", $progname, $sp)
              if $verbose >= 1;
            next;
        }
        my(@files) = grep !/^\./, readdir THEDIR;
        my ($file);
        foreach $file (@files) {
            if ($file =~ /^(csm\.director\.(agent|server)).+\.rpm$/ ||
                $file =~ /^((Dir|ITD)(Agent|Server)).+\.rpm$/ ||
                $file =~ /^(RAIDLxAg).*\.rpm$/ ||
                $file =~ /^(MPAAgent).*\.rpm$/) {
                my($fullpath) = catdir($sp, $file);
                updateRPMKey($1, $fullpath);
            }
        }
        closedir(THEDIR);
    }

    if ($verbose >= 1) {
      NodeUtils->message('I', "IMsgLocatedRPMS");
      if (!keys %RPMS) { NodeUtils->message('I', "IMsgNone"); }
      else
      {
	for my $pkg (keys %RPMS)
	{
            my($version, $release, $file) = @{$RPMS{$pkg}};
	    print "$pkg: $version, $release, $file\n";
	}
      }
      print "\n\n";
  }
}


######################################################################
# inst_type
#     Determine if package should be installed or upgraded.
#     returns: "install" (if pkg is not yet installed, but an acceptable pkg is available)
#              "update"  (if pkg is installed, but a newer pkg is available)
#              "none"    (if pkg is installed and acceptable, and nothing newer is available)
#              "NoRPM"   (if pkg is not installed or is backlevel, and no pkg is available)
#              "RPMbacklevel" (if pkg is not installed or is backlevel, and available pkg is backlevel)
######################################################################
sub inst_type {
    my($pkgname,$required_version) = @_;
    my($install_type) = "not set";

    my($required_pkg_is_available) = 1; # initial assumption
    my ($available_version, $available_spin, $available_file);
    if (exists($RPMS{$pkgname})) {
        ($available_version, $available_spin, $available_file) = @{$RPMS{$pkgname}}; 
        if (ArchiveUtils->testVersion($available_version, "<", $required_version)) { 
            $required_pkg_is_available = 0;
            $install_type = "RPMbacklevel";
        }
    } else {
        $required_pkg_is_available = 0;
        $install_type = "NoRPM";
    }

    my($installed_pkg_is_ok) = 1; # initial assumption
    if (exists($INSTALLED{$pkgname})) {
        my ($installed_version, $installed_spin) = @{$INSTALLED{$pkgname}};
        if (ArchiveUtils->testVersion($installed_version, "<", $required_version)) {
            $installed_pkg_is_ok = 0;
        }

        if ($required_pkg_is_available) {
            if (ArchiveUtils->testVersion($installed_version, "<", $available_version)) { 
                $install_type = "upgrade"; # pkg is installed but available pkg is newer, so upgrade
            } else {
                $install_type = "none"; # pkg is installed at acceptable version
            }
        } else {
            if ($installed_pkg_is_ok) {
                $install_type = "none"; # pkg is installed at acceptable version
            } else {
                # installed pkg is NOT ok, and required version is NOT available 
                # $install_type should already be set to "RPMbacklevel" or "NoRPM"
            }
        }
    } else {
        if ($required_pkg_is_available) {
            $install_type = "install"; # pkg is not installed yet, but is available
        } else {
            # pkg is NOT installed yet, and is required version is NOT available 
            # $install_type should already be set to "RPMbacklevel" or "NoRPM"
        }
    }

    return $install_type;
}


######################################################################
# install_rpm
#     Install/Upgrade RPM file
######################################################################
sub install_rpm {
    my($pkgname, $filename, $install_type) = @_;

    # Figure out what is being installed:
    # File only -> get pkgname
    # Name only -> get file
    # both      -> assume they match
    # neither   -> error
    if ($filename ne "" && $pkgname eq "") { # Raw RPM file, get package
        my($queryp) = "-p $filename";
        print "$DirectorUtils::rpmquery $queryp $DirectorUtils::queryfmt\n" if $verbose >= 3;
        chomp(my(@rpmout)=`$DirectorUtils::rpmquery $queryp $DirectorUtils::queryfmt`);
        my($rc) = $? >> 8;
        if ($rc != 0) {
            return;
        }
        my(@rest);
        ($pkgname, @rest) = split(/ /,@rpmout);
    }
    elsif ($pkgname ne "" && $filename eq "") { # Package, get file
        if (exists($RPMS{$pkgname})) {
            my($version,$release,$fn) = @{$RPMS{$pkgname}};
            $filename = $fn;
        }
        else {
            NodeUtils->message('E', "EMsgNoRPMAvailable", $pkgname);
            return 1;
        }
    }
    elsif ($pkgname eq "" && $filename eq "") { # bogus, nothing asked for
        NodeUtils->message('E', "EMsgNoPackageOrFile", $install_type);
        return 1;
    }

    if ($install_type eq "install" || $install_type eq "") {
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I',"IMsgInstallingRPM", $progname,$pkgname);
        $install_type = "install";
    }
    elsif ($install_type eq "upgrade") {
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgUpgradingRPM",
                                  $progname, $pkgname);
    } else {
        NodeUtils->message('E', "EMsgInstallRPMerror",
                           $pkgname, $filename, $install_type);
        return 0;
    }

    my $prefix_option = "";
    our $TWG_DIR = DirectorUtils::getTWGDIR();

    # Set Prefix option to install CSM Extension files in the same location as
    # Director RPMs
    if ($TWG_DIR ne "") {
	$prefix_option = "--prefix $TWG_DIR";
    }

    print "\n$RPM --$install_type $prefix_option $filename\n" if $verbose >= 3;
    my $inst_output = `/bin/sh -c "$RPM --$install_type $prefix_option $filename 2>&1"`;

    my ($rc) = $? >> 8;
    if ($rc != 0) {
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgFailure");
        print("$inst_output\n");
        return 1;
    }
    else {
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgSuccess");
    }

    $installed_count++;
    return 0;
}


######################################################################
# uninstall_rpm
#     locate RPM in searchpath and uninstall it
######################################################################
sub uninstall_rpm {
    my($pkgname) = @_;
    NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common','I',
                              "IMsgUninstallRPM", $progname, $pkgname);
    if ($verbose >= 3) {
        print         "$RPM --erase $pkgname  ... ";
    }
    my $inst_output = `$RPM --erase $pkgname`;
    my ($rc) = $? >> 8;
    if ($rc) {
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgFailure");
        print("$inst_output\n");
    }
    else {
        NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                  'I', "IMsgSuccess");
    }
    return $rc;
}


######################################################################
# locateAndCopy
#     locate Dir Agent rpms and copy if necessary to $csmlcspkgs
######################################################################
sub locateAndCopy {
    my($pkg, $required_level) = @_;

    # Copy RPM to CSM package distribution directory
    if (exists($RPMS{$pkg})) { # Can we find the RPM?
        my ($v, $r, $file) = @{$RPMS{$pkg}};
        if (ArchiveUtils->testVersion($v, ">=", $required_level)) { #Is it the right level?
            my $dest = catdir($csmlcspkgs, basename($file));
            if ($file ne $dest) {
                NodeUtils->message('I', "IMsgCopyRPM", $progname, $pkg, $csmlcspkgs);
                my ($output) = `/bin/cp -p $file $dest`;
                if ($?) {
                    NodeUtils->message('E', "EMsgRPMcopyError");
                    print("\t$output\n");
                }
            }
        } else { # RPM found, but backlevel
            NodeUtils->message('E', "EMsgRPMbacklevel",
                               $pkg, $required_level, $v);
        }
    } else {
        NodeUtils->message('E', "EMsgMissingRPM", $pkg, $csmlcspkgs);
    }
}



######################################################################
######################################################################
# Mainline code
######################################################################
######################################################################

######################################################################
# Setup
######################################################################
&parse_args;   # parse command line
&set_defaults; # setup default values

$pkgs_to_query =
  "csm.core ".                   # pre-reqs
  "csm.server ".                 # System is CSM MS
  "csm.client ".                 # System is managed node
  "csm.director.agent csm.director.server ". # lcs ext packages
  "ITDServer ITDAgent";          # our Director pre-reqs

$RPMARCH = ArchiveUtils->get_PkgArchitecture;
if ( $RPMARCH =~ /i.86/) {
    $RPMARCH = "i386"; # normalize i586, i686, etc.
    $pkgs_to_query .= " DirAgent RAIDLxAg MPAAgent"; # additional Director rpms on i386
} else {
    $RPMARCH = "ppc"; # must be power pc architecture
}

# locate pre-req / other sw installed, packages to install
%INSTALLED = DirectorUtils::determine_installed_sw($pkgs_to_query);
&catalogRPMS; # populate %RPMS

# If csm.core is installed, then some additional functions are available
our $is_mgmt_svr = 0;
our $is_node = 0;
if (exists($INSTALLED{"csm.core"})) {
    $is_mgmt_svr = NodeUtils::isMgmtSvr();
    $is_node = NodeUtils::isNode();
}


if ($is_mgmt_svr && NodeUtils::csm_license_valid() == 0) {
    NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                              'E', "EMsgCSMLicenseInvalid");
    my ($extension_name) = NodeUtils->getMessageFromCat($MSGCAT, $MSGMAPPATH, 'common',
                                                        "IMsgExtensionName");
    NodeUtils->message('E', "EMsgPrerequisiteNotMet",
                       $extension_name);
    exit 95;
}

if ($try_install_server || $try_install_everything) {
    if (exists($INSTALLED{"ITDServer"})) {
        &install_server; # try to install server-side LCS extension 
    } else {
        NodeUtils->message('I', "IMsgNotDirectorServer", $progname);
        NodeUtils->message('I', "IMsgSkippedSvrInstalls", $progname);
        if ($try_install_server) { $pkgs_not_installed++; }
    }
}


# Try to install ITDAgent and CSM Agent Extension
if ($try_install_everything || $try_install_agent) {
    if ($is_mgmt_svr || $is_node) {
        &install_agent; # try to install Dir Agent and agent-side stuff
    } else {
        NodeUtils->message('I', "IMsgNotCSMsystem", $progname);
        NodeUtils->message('I', "IMsgSkippedAgentInstalls", $progname);
        if ($try_install_agent) { $pkgs_not_installed++; }
    }
}


# If system is a CSM MS, we'll copy the latest and greatest IBM Director Agent rpms to
# /opt/csm/lcsext/packages (which is a symbolic link to the CSM MS's package directory
# down under /csminstall). This exercise only really does anything if user invoked
# addlcsext with -p flag and there are newer Agent rpm(s) in that path than we already
# have in the packages directory.
if ($is_mgmt_svr) {
    #NodeUtils->messageFromCat($MSGCAT, $MSGMAPPATH, 'common', 'I', "IMsgCSMmgmtSrvr", $progname);
    my ($version, $release) =  @{$INSTALLED{"csm.server"}};
    if (ArchiveUtils->testVersion($version, ">=", $required_csm_version)) {
        &locateAndCopy( "ITDAgent", "4.12");
        if ( $RPMARCH eq "i386") {
            &locateAndCopy( "DirAgent", "4.12");
            &locateAndCopy( "RAIDLxAg", "4.11");
            &locateAndCopy( "MPAAgent", "4.11");
        }
    } else {
        NodeUtils->message('E', "EMsgInvalidPrerequisiteLevel",
                           "csm.server", $required_csm_version);
        NodeUtils->message('E', "EMsgDirectorRPMSnotCopied",
                           $director_agent_name);
    }
}


# (Re)start Director Server or Agent
if ($director_stopped || $installed_count || $director_installed_or_upgraded) {
    &start_director;
}

exit $pkgs_not_installed;
